/****************************************************************************\
*
*				hpidd.c
*
*	This module determines the HP laptop product IDs.
*
*	00/06/30 lnc	- V1.12, updated for Jaws.
*	00/02/17 bw	- V1.11, updated for Oddjob 2, Sterling.
*	98/06/09 lnc	- V1.10, updated for Galileo 2, Voyager 2, Oddjob.
*	98/06/05 lnc	- V1.09, updated for Apollo 2.
*	98/06/02 lnc	- V1.08, removed GetDMIdata() and SMI function
*			method from iGet_Prod_ID(); reorganized routine order.
*	98/05/20 lnc	- V1.07, modified HP_ID_Table[] and vCheck_HP_ID()
*			to distinguish Tillamook from Deschutes and OB3100
*			from OB2100 for use of DMI tables; vCheck_HP_ID() now
*			gets BIOS ID and model name to use with added
*			routine vTune_HP_ID().
*	98/05/01 lnc	- V1.06, renamed GetProdID to iGet_Prod_ID().
*	98/03/01 lnc	- V1.05, added modifications to GetProdID() to
*			directly access DMI structures from the SMBIOS
*			structure table; modified renamed HP_ID_Table to
*			set appropriate index for product ID returned from
*			GetProdID() through direct access to DMI structures,
*			SMBIOS function access to DMI structures, or
*			INT 15h, function 4DD4h.
*	98/02/18 lnc	- V1.04, modified key IDs of HpIDTable, and
*			iDo_Int15() for new version of INT 15 - qCX.MY.ZZ,
*			BIOS version 37a.
*	98/02/11 lnc	- V1.03, modified key IDs of HpIDTable, and
*			iDo_Int15() for new version of INT 15 - q.CX.MY.ZZ,
*			BIOS version 35a.
*	98/01/19 lnc	- V1.02, modified key IDs of HpIDTable, debug for
*			iDo_Int15().
*	97/12/10 lnc	- V1.01, modified for Medium model, added new
*			catagory to LookUp structure for a this test program.
*	97/12/08 bw	- V1.00, created.
*
\****************************************************************************/
#include <stdio.h>
#include <string.h>

#include "model.h"

#define MAXBUFSIZE 8192

#define	BID_BA		0x4241
#define	BID_CB		0x4342
#define	BID_CC		0x4343
#define	BID_CD		0x4344
#define	BID_CE		0x4345
#define	BID_CF		0x4346
#define	BID_CG		0x4347
#define	BID_CH		0x4348
#define	BID_CI		0x4349
#define	BID_CJ		0x434A
#define BID_CK		0x434B
#define BID_CL		0x434C
#define	BID_C0		0x4330
#define BID_EA		0x4541
#define BID_FA		0x4641

typedef struct
	{
	unsigned inSig;
	int key;
	} LookUp;

typedef unsigned char BYTE;
typedef unsigned short WORD;

int iGet_Prod_ID (void);
static BYTE far *bfpFind_Dmi_Table(void);
static int iGetPlatformId(char far *cpPtr);
static void vCheck_HP_ID(BYTE far *fpntr);
static char far *cfpGet_String(int n, BYTE far *fpntr);
static void vTune_HP_ID(void);
static BYTE far *bfpGet_Next_Structure(BYTE far *s);
static unsigned uDo_Int15 (void);

static LookUp HP_ID_Table[] =
	{
	{0x0301, OB4000},	 //  1 Pegasus
	{0x0401, OB5000}, 	 //  2 Tristar
	{0x0402, OB5500}, 	 //  3 Quadstar
	{0x0403, OB5700},	 //  4 AA, Polaris
	{0x0404, OB2000}, 	 //  5 AA, Reno
	{0x0501, OB800O},	 //  6 Orion
	{0x0502, OB800M},	 //  7 AB, Mercury, MMX
	{0x0601, OB3000},	 //  8 CA, Magellan
	{BID_BA, OBSOJOURN},	 //  9 BA, Sputnik

	{BID_CB, OB7100T},	 // 10 CB, Galileo 1, Tillamook
	{BID_CC, OB7100D},	 // 11 CC, Galileo 1, Deschutes
	{BID_CH, OB7150},	 // 12 CH, Galileo 2, Deschutes, 300 Mhz
	{BID_CD, OB4100T},	 // 13 CD, Voyager 1, Tillamook
	{BID_CE, OB4100D},	 // 14 CE, Voyager 1, Deschutes
	{BID_CI, OB4150A},	 // 15 CI, Voyager 2, Deschutes, 300 Mhz
	{BID_CF, OB3100},	 // 16 CF, Apollo 1, Tillamook, 266 Mhz
	{BID_C0, OB2100T},	 // 17 CF, Apollo 1, Tillamook, 233 Mhz
	{BID_CG, OB2100D},	 // 18 CG, Apollo 2, Deschutes, 266 Mhz
	{BID_CJ, OB900A},	 // 19 CJ, Oddjob, Deschutes, 266 Mhz
	{BID_CK, OB4150B},	 // 20 CK, Voyager 3.9, 4, 4.2
	{BID_CL, OB900B},	 // 21 CL, Oddjob 2, 2.5, 2.7
	{BID_EA, OB6000},	 // 22 EA, Sterling
	{BID_FA, OB500},	 // 23 FA, Jaws
	{0,	 0}		 // Marks end of table
	};

static char HP_Str[] = "HP OmniBook PC";
#define HP_STR_BSZ	(sizeof HP_Str) - 3

static char Model_Bf[16] = "";

static unsigned uBIOS_Sig = 0;
static unsigned uProdID = 0;
static unsigned uOmniBook = 0;

/****************************************************************************\
*				iGet_Prod_ID
*
*	Identify the computer, first trying the DMI tables and if that
*	fails then trying INT 15 function 4DD4.
*
*	Output:
*		return -	-1,		unknown
*				-2,		Vectra
*				OB4000,		OB 4000
*				OB5000,		OB 5000
*				OB5500,		Quadstar
*				OB5700,		Polaris
*				OB2000,		Reno
*				OB800O,		Orion0
*				OB800M,		Mercury, MMX
*				OB3000,		Magellan
*				OBSOJOURN,	Sputnik
*				OB7100T,	Galileo 1, Tillamook
*				OB7100D,	Galileo 1, Deschutes
*				OB7150,		Galileo 2, Deschutes, 300 Mhz
*				OB4100T,	Voyager 1, Tillamook
*				OB4100D,	Voyager 1, Deschutes
*				OB4150A,	Voyager 2, Deschutes, 300 Mhz
*				OB4150B,	Voyager 3.9, 4, 4.2
*				OB3100,		Apollo 1, 266 Mhz
*				OB2100T,	Apollo 1, 233 Mhz
*				OB2100D,	Apollo 2, Deschutes, 266 Mhz
*				OB900A,		Oddjob, Deschutes, 266 Mhz
*				OB900B,		Oddjob 2
*				OB6000		Sterling
*				OB500		Jaws.
\****************************************************************************/
int
iGet_Prod_ID(void)
	{
	BYTE far *fpntr;
	unsigned int i;

	uBIOS_Sig = 0;
	uProdID = 0;
	uOmniBook = 0;

// first try accessing DMI tables directly
	if ((fpntr = bfpFind_Dmi_Table()) != NULL)
		{
		do
			{
			vCheck_HP_ID(fpntr);
			if (uOmniBook && uBIOS_Sig)
				{
				vTune_HP_ID();	// tune for crummy DMI design

				for (i = 0; HP_ID_Table[i].inSig != 0; i++)
					if (HP_ID_Table[i].inSig == uBIOS_Sig)
					      return HP_ID_Table[i].key; // OK

				break;	// bad product ID, NOT found in table
				}
			}
		while ((fpntr = bfpGet_Next_Structure(fpntr)) != 0);
		}

// DMI did not work, use INT 15h , function 4DD4h
	if ((uProdID = uDo_Int15()) < 0)
		return (int) uBIOS_Sig;			// NOT HP OmniBook

	for (i = 0; HP_ID_Table[i].inSig != 0; i++)
		if (HP_ID_Table[i].inSig == uBIOS_Sig)
			return HP_ID_Table[i].key;	// OK return

	return -1;					// NOT found in table
	}

/****************************************************************************\
*				bfpFind_Dmi_Table
*
*	This function searches BIOS segment F000h for the signature "_DMI_"
*	and returns a pointer to the SMBIOS structure table.
*
*	Output:
*		return -	NULL, "_DMI_" signature notfound
*				pointer to SMBIOS structure table.
\****************************************************************************/
static BYTE far *
bfpFind_Dmi_Table(void)
	{
	BYTE far *pDmiTable;

	_asm	{
		push es
		push bx
		push cx
		mov ax, 0xf000		; start at beginning of F000h segment
		mov es, ax
		xor bx, bx

	Loop1:
	; look for "_DMI_" signature
		cmp WORD PTR es:[bx], 'D_'	; "_D"
		jne NotYet

		cmp WORD PTR es:[bx + 2], 'IM'	; "MI"
		jne NotYet

		cmp BYTE PTR es:[bx + 4], '_'	; "_"
		je FoundDmi

	NotYet:
		add bx, 0x10		; next paragraph still in segment
		jnc Loop1		; yes

		xor bx, bx		; no, signature not found,
		xor cx, cx		 ; return NULL pointer
		jmp Done

	FoundDmi:
	; found it, get 32-bit real mode physical starting address of
	;read-only SMBIOS structure table
		mov cx, WORD PTR es:[bx + 9]	; get segment
		shl cx, 1
		shl cx, 1
		shl cx, 1
		shl cx, 1
		mov bx, WORD PTR es:[bx + 8]	; get offset
		and bx, 0xff

	Done:
		mov WORD PTR pDmiTable, bx	; save offset
		mov WORD PTR pDmiTable[2], cx	; save segment
		pop cx
		pop bx
		pop es
		}

	return pDmiTable;
	}

/****************************************************************************\
*				iGetPlatformId
*
*	Input:
*		cpPtr -		pointer to a structure in the SMBIOS
*				structure table.
*
*	Output:
*		return -	-1, not an Omnibook
*				xy, 2-character platform ID (iOmniBook == 1).
\****************************************************************************/
static int
iGetPlatformId(char far *cpPtr)
	{
	int iRtn;

	while (*(cpPtr++) != '.')
		;

	if (*cpPtr == 'M')
		{
		uOmniBook = 1;
		iRtn = ((int) *(cpPtr - 3) << 8);
		iRtn |= (int) *(cpPtr - 2);
		return iRtn;
		}

	return -1;
	}

/****************************************************************************\
*				vCheck_HP_ID
*
*	This routine checks the SMBIOS structure table for structures of
*	type 1h and 80h for Product Name and Notebook ID, respectively.
*
*	Input:
*		fpntr -		pointer to a structure in the SMBIOS
*				structure table.
*
*	Output:
*		sets uBIOS_Sig to BIOS ID if structure type 0h is found,
*		sets uOmniBook to 1 if structure type 1h is found,
*		sets uProdID to product ID if structure 80h is found:
*			7, Galileo
*			8, Voyager
*			9, Apollo.
\****************************************************************************/
static void
vCheck_HP_ID(BYTE far *fpntr)
	{
	char far *fstr;
	int iTmp;

	switch (*fpntr)
		{
		case 0x00:
		// Type 0h: BIOS Information
		// get BIOS version
		        iTmp = iGetPlatformId(cfpGet_String(0x5, fpntr));

			if (iTmp != -1)
				uBIOS_Sig = (unsigned) iTmp;

			else
				uBIOS_Sig = 0;

			return;

		case 0x01:
		// Type 1h: System Information
		// check product name
			if (_fstrcmp(cfpGet_String(0x5, fpntr), HP_Str) == 0)
				uOmniBook = 1;	// Product Name checks out

		// get model name
			if (_fstrncmp((fstr = cfpGet_String(0x6, fpntr)),
						HP_Str, HP_STR_BSZ) == 0)
				_fstrcpy(Model_Bf, fstr + HP_STR_BSZ);

			return;

		case 0x80:
		// Type 80h: HP ID
		// get notebook ID
			uProdID = *(fpntr + 6);
			return;
		}
	}

/****************************************************************************\
*				cfpGet_String
*
*	This routine finds a string in the string area of an SMBIOS structure.
*
*	Input:
*		n -		offset of string type, determining string
*				number
*		fpntr -		pointer to a structure in the SMBIOS
*				structure table.
*
*	Output:
*		returns -	NULL, length byte is zero
*				pointer to a string in a structure of
*				finite length; string may be a NULL string.
\****************************************************************************/
static char far *
cfpGet_String(int n, BYTE far *fpntr)
	{
	unsigned i, j;
	BYTE far *p;

	if ((j = *(fpntr + 1)) == 0) 
		return NULL;		// length of formatted area is zero

	p = fpntr + j;			// point to start of string section
	j = *(fpntr + n);		// get string number

	for (i = 1; i < j; i++)
		while (*p++)
			;		// move to start of appropriate string

	return (char far *) p;
	}

/****************************************************************************\
*				vTune_HP_ID
*
*	This routine splits the 'CF' from the OB2100 & 3100 into 2 IDs
*	
\****************************************************************************/
static void
vTune_HP_ID(void)
	{
	switch (uBIOS_Sig)
		{
		case BID_CF:
			uBIOS_Sig = (Model_Bf[0] == '2') ? BID_C0 : BID_CF;
			return;
		}
	}

/****************************************************************************\
*				bfpGet_Next_Structure
*
*	This routine searches to the end of a structure for the start of
*	the next structure in the SMBIOS structure table.
*
*	Input:
*		s -		pointer to a structure in the SMBIOS
*				structure table.
*
*	Output:
*		returns -	NULL, length byte is zero
*				pointer to next structure.
\****************************************************************************/
static BYTE far *
bfpGet_Next_Structure(BYTE far *s)
	{
	unsigned len;

// get length of formatted area
	if ((len = *(s + 1)) == 0)
		return NULL;		// length of formatted area is zero

// point to string area and move past end
	for (s += len; *s != '\0' || *(s + 1) != '\0'; s++)
		;

	s += 2;				// point at next structure
	return s;
	}

/****************************************************************************\
*				uDo_Int15
*
*	Do INT 15 function 4DD4h.
*
* 	Output:
*		return -	-1, 	Not HP OmniBook, int 15 funct 4DD4
*					not supported
*				-2,	Vectra  (int 15 funct 4DD4 returned
*					HP div ID != MCD)
*				nn,	For old OB, nn = chip set + model;
*					for newer OB, nn = 2 letter platform
*					identifier.
*
*	Note - OB800, OB3000 and probably older versions of the Omnibook
*	use the old version of INT 15h.
\****************************************************************************/
static unsigned
uDo_Int15(void)
	{
	unsigned axreg;

	_asm
		{
		push	es
		push	di
		mov	ax, 0x4DD4
		int	15h

		cmp	bx, 'HP'	// BX == 0x4850 ?
		je	OldOB		// Yes, old version of INT 15h

		cmp	bx, 'hp'	// BX == 0x6870	?
		jne	NotOB		// NO, error, not a OmniBook

	// ES:DI -> <blank>AA.UX.YY, qAA.UX.YY, cAA.UX.YY, bAA.UX.YY,
	// or AA.UX.YY
		mov	al, es:[di]	// YES, check first character for
					 // ' ', 'q', 'c', or 'b'
		cmp	al, ' '
		je	Prfx

		cmp	al, 'q'
		je	Prfx

		cmp	al, 'c'
		je	Prfx

		cmp	al, 'b'
		jne	NoPrfx

	Prfx:
		inc	di

	NoPrfx:
		mov	ax, es:[di]	// get AA field
		xchg	ah, al
		cmp	byte ptr es:[di + 3], 'M'  // U field == M for MCD ?
		je	Exit		// YES, MCD

		mov	ax, -2		// NO, must be Vectra
		jmp	Exit

	OldOB:
		mov	ax, cx		// CH = chip set, CL = model
		jmp	Exit		// Magellan, Mercury, Orion, etc.

	NotOB:
		mov	ax, -1		// not Hewlett-Packard product

	Exit:
		pop	di
		pop	es
		mov	axreg, ax
		}

	return axreg;
	}